package main

import (
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"happy-book/internal/config"
	"happy-book/internal/repository"
	cache2 "happy-book/internal/repository/cache"
	"happy-book/internal/repository/dao"
	"happy-book/internal/service"
	"happy-book/internal/web/handler"
	"happy-book/internal/web/jwt"
	"happy-book/internal/web/middleware"
	"happy-book/pkg/ratelimit"
	"strings"
	"time"
)

func main() {
	//初始化数据库
	db := initDb()

	//初始化Web服务器
	server := initWebServer()

	//初始化UserHandler
	initUserHandler(db, server)

	//启动Web服务器,并监听端口
	println("web server started~")

	err := server.Run(":8080")
	if err != nil {
		panic(err)
	}
}

// 初始化数据库
func initDb() *gorm.DB {
	//指定数据库连接串
	dsn := config.Config.DBConfig.DSN

	//创建数据库连接
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	return db
}

// 初始化Web服务器
func initWebServer() *gin.Engine {
	//创建gin web服务器
	server := gin.Default()

	//跨域配置
	corsConfig(server)

	//注册middleware
	registerMiddlewares(server)

	return server
}

// 配置跨域策略
func corsConfig(server *gin.Engine) {
	//注册cors middleware跨域中间件,处理浏览器的preflight请求
	//针对浏览器发送的请求,如果协议、域名或者端口中任意一项与后端不一致,都是跨域请求
	//浏览器发起跨域请求时,会先发送一个preflight预检请求,它的请求方式是Options,会将真实请求的Origin、Method、Request Header等信息发送给后端,判断后端是否允许访问
	//如果后端允许了preflight请求,则浏览器会发送真实的请求,否则本次跨域请求会被禁止
	server.Use(cors.New(cors.Config{
		//允许跨域请求携带用户认证相关信息,如Cookie、SSL或Http鉴权等信息
		AllowCredentials: true,

		//允许跨越的origin(请求来源)
		AllowOriginFunc: func(origin string) bool {
			return strings.HasPrefix(origin, "http://localhost")
		},

		//允许携带的请求头
		//允许 AuthorizationHeader,用于JWT鉴权
		AllowHeaders: []string{"Content-Type", "Authorization"},

		//允许暴露JWT相关Header
		ExposeHeaders: []string{jwt.TokenHeaderKey},

		//preflight请求的有效期
		MaxAge: 12 * time.Hour,

		//允许跨越的请求方式,默认允许所有
		//AllowMethods: []string{"POST", "GET", "PUT", "DELETE"},
	}))
}

// 注册gin middleware
func registerMiddlewares(server *gin.Engine) {
	//注册限流中间件
	redisConfig := config.Config.RedisConfig
	cmd := redis.NewClient(&redis.Options{
		Addr:     redisConfig.Addr,
		Password: redisConfig.Password,
		DB:       redisConfig.DB,
	})
	limiter := ratelimit.NewRedisSlidingWindowLimiter(cmd, time.Second, 1000) //TODO 限流的滑动窗口大小和阈值最好通过压测得到
	server.Use(middleware.NewRateLimitMiddleware(limiter).RateLimitByIP())

	//注册登录校验Middleware
	server.Use(middleware.CheckLogin())
}

// 初始化UserHandler
func initUserHandler(db *gorm.DB, server *gin.Engine) {
	//依赖注入
	userDAO := dao.NewUserDao(db)
	redisConfig := config.Config.RedisConfig
	cmd := redis.NewClient(&redis.Options{
		Addr:     redisConfig.Addr,
		Password: redisConfig.Password,
		DB:       redisConfig.DB,
	})
	cache := cache2.NewUserCache(cmd)
	repo := repository.NewUserRepo(userDAO, cache)
	svc := service.NewUserService(repo)
	hdl := handler.NewUserHandler(svc)

	//注册路由
	hdl.RegisterRoutes(server)
}
